/* ** por compatibilidad se omiten tildes **
================================================================================
 TRABAJO PRACTICO 3 - System Programming - ORGANIZACION DE COMPUTADOR II - FCEN
================================================================================
  definicion de estructuras para administrar tareas
*/

#include "tss.h"
#include "mmu.h"
#include "screen.h"
#include "game.h"
#include "sched.h"

tss tss_inicial;
tss tss_idle;

tss tss_zombisA[CANT_ZOMBIS];
tss tss_zombisB[CANT_ZOMBIS];

//
// Constantes que se definen para completar las estructuras. 
// DS 
//
#define EFLAGS  0x00000202
#define CS      0x0040			// selector de codigo de la gdt de ring 0
#define DS      0x0050			// selector de datos la gdt de ring 0
#define CS_3      0x004B		// selectos de codigo de la gdt de ring 3 con el rpl adecuado
#define DS_3      0x005B		// selector de datos de la gdt de ring 3 con rpl adecuado

#define ES      DS
#define FS      DS
#define ES_3      DS_3
#define FS_3      DS_3

#define GS      0x0060
#define SS      DS
#define SS_3      DS_3


// sirve para guardar los cr0, cr2 y cr4 de las tareas
// ya que no se guardan en el contexto
unsigned int cr0tareas_rojo[CANT_ZOMBIS];
unsigned int cr2tareas_rojo[CANT_ZOMBIS];
unsigned int cr4tareas_rojo[CANT_ZOMBIS];

unsigned int cr0tareas_azul[CANT_ZOMBIS];
unsigned int cr2tareas_azul[CANT_ZOMBIS];
unsigned int cr4tareas_azul[CANT_ZOMBIS];

// "getters" de los registros cr que no se guardan en los contextos
unsigned int get_cr0tarea_rojo(unsigned int id) {
	return cr0tareas_rojo[id];
}

unsigned int get_cr2tarea_rojo(unsigned int id) {
	return cr2tareas_rojo[id];
}

unsigned int get_cr4tarea_rojo(unsigned int id) {
	return cr4tareas_rojo[id];
}

unsigned int get_cr0tarea_azul(unsigned int id) {
	return cr0tareas_azul[id];
}

unsigned int get_cr2tarea_azul(unsigned int id) {
	return cr2tareas_azul[id];
}

unsigned int get_cr4tarea_azul(unsigned int id) {
	return cr4tareas_azul[id];
}

// "setters" de los registros cr que no se guardan en los contextos
void set_cr0tarea_rojo(unsigned int id, unsigned int valor) {
	cr0tareas_rojo[id] = valor;
}

void set_cr2tarea_rojo(unsigned int id, unsigned int valor) {
	cr2tareas_rojo[id] = valor;
}

void set_cr4tarea_rojo(unsigned int id, unsigned int valor) {
	cr4tareas_rojo[id] = valor;
}

void set_cr0tarea_azul(unsigned int id, unsigned int valor) {
	cr0tareas_azul[id] = valor;
}

void set_cr2tarea_azul(unsigned int id, unsigned int valor) {
	cr2tareas_azul[id] = valor;
}

void set_cr4tarea_azul(unsigned int id, unsigned int valor) {
	cr4tareas_azul[id] = valor;
}


void guardar_crs() {
	if (es_actual_rojo()) {
		unsigned int id_zombi_activo = obtener_zombi_actual_rojo();
		set_cr0tarea_rojo(id_zombi_activo, rcr0());
		set_cr4tarea_rojo(id_zombi_activo, rcr4());
		set_cr2tarea_rojo(id_zombi_activo, rcr2());
	} else {
		unsigned int id_zombi_activo = obtener_zombi_actual_azul();
		set_cr0tarea_azul(id_zombi_activo, rcr0());
		set_cr4tarea_azul(id_zombi_activo, rcr4());
		set_cr2tarea_azul(id_zombi_activo, rcr2());

	}

}

unsigned int  dame_cr3(int nroZombie, int color) {
	if (color == 0) {
		return  tss_zombisA[nroZombie].cr3;
	} else {
		return tss_zombisB[nroZombie].cr3;
	}
}

tss get_tss(unsigned int zombi, unsigned int color) {
	if (color == 0) {
		return tss_zombisA[zombi];
	} else {
		return tss_zombisB[zombi];
	}
}
// configurar la tarea inicial
void configurar_tarea_inicial() {
  
  unsigned int dirTSS = (unsigned int) &tss_inicial;
  
  // Se configura la dirección base del TSS para la tarea inicial (la
  // entrada 16 en la GDT corresponde al descriptor de la tarea inicial).
  gdt[GDT_IDX_INIT].base_0_15  = (unsigned short) dirTSS;
  gdt[GDT_IDX_INIT].base_23_16 = (char) (dirTSS >> 16);
  gdt[GDT_IDX_INIT].base_31_24 = (char) (dirTSS >> 24);

  
  // como la tarea inicial no realiza nada no hace falta setear la tss. 
}


// configura la tarea zombi
void configurar_tarea_zombi_rojo(int nroZombi) {
 
  // obtenemos la direccion de la tarea iddle
  unsigned int dirTSS = (unsigned int) &tss_zombisA[nroZombi];

  // se configura el descriptor del segmento de tarea
  gdt[GDT_IDX_ROJO_1 + nroZombi].base_0_15  = (unsigned short)  dirTSS;
  gdt[GDT_IDX_ROJO_1 + nroZombi].base_23_16 = (char) (dirTSS >> 16);
  gdt[GDT_IDX_ROJO_1 + nroZombi].base_31_24 = (char) (dirTSS >> 24);

  //
  // La tarea inactiva usa el mismo Directorio de Páginas del Kernel.
  tss_zombisA[nroZombi].cr3    = (unsigned int) ajustar_mmu_dir_zombi(nroZombi, 0, dame_tipo_zombi_rojo(nroZombi));

  tss_zombisA[nroZombi].eip    = INIT_VIRTUAL_TASK_CODE;
  tss_zombisA[nroZombi].eflags = EFLAGS;
  

  // Para la pila de esta tarea, pedimos una página libre.
  tss_zombisA[nroZombi].esp = INIT_VIRTUAL_TASK_CODE + PAGE_SIZE;

  tss_zombisA[nroZombi].esp0 = malloc_page() + PAGE_SIZE;
  tss_zombisA[nroZombi].ss0 = SS;

  tss_zombisA[nroZombi].ebp    = tss_zombisA[nroZombi].esp;
  tss_zombisA[nroZombi].es     = ES_3;
  tss_zombisA[nroZombi].cs     = CS_3;
  tss_zombisA[nroZombi].ss     = SS_3;
  tss_zombisA[nroZombi].ds     = DS_3;
  tss_zombisA[nroZombi].fs     = FS_3;
  tss_zombisA[nroZombi].gs     = DS_3;


}

// configura la tarea zombi
void configurar_tarea_zombi_azul(int nroZombi) {
 
  // obtenemos la direccion de la tarea iddle
  unsigned int dirTSS = (unsigned int) &tss_zombisB[nroZombi];

  // se configura el descriptor del segmento de tarea
  gdt[GDT_IDX_AZUL_1 + nroZombi].base_0_15  = (unsigned short)  dirTSS;
  gdt[GDT_IDX_AZUL_1 + nroZombi].base_23_16 = (char) (dirTSS >> 16);
  gdt[GDT_IDX_AZUL_1 + nroZombi].base_31_24 = (char) (dirTSS >> 24);

  //
  // La tarea inactiva usa el mismo Directorio de Páginas del Kernel.
  tss_zombisB[nroZombi].cr3    = (unsigned int) ajustar_mmu_dir_zombi(nroZombi, 1, dame_tipo_zombi_azul(nroZombi));

  tss_zombisB[nroZombi].eip    = INIT_VIRTUAL_TASK_CODE;
  tss_zombisB[nroZombi].eflags = EFLAGS;
  

  // Para la pila de esta tarea, pedimos una página libre.
  tss_zombisB[nroZombi].esp = INIT_VIRTUAL_TASK_CODE + PAGE_SIZE;

  tss_zombisB[nroZombi].esp0 = malloc_page() + PAGE_SIZE;
  tss_zombisB[nroZombi].ss0 = SS;

  tss_zombisB[nroZombi].ebp    = tss_zombisB[nroZombi].esp;
  tss_zombisB[nroZombi].es     = ES_3;
  tss_zombisB[nroZombi].cs     = CS_3;
  tss_zombisB[nroZombi].ss     = SS_3;
  tss_zombisB[nroZombi].ds     = DS_3;
  tss_zombisB[nroZombi].fs     = FS_3;
  tss_zombisB[nroZombi].gs     = DS_3;

}


// configuracion de la tarea idle
void configurar_tarea_idle() {
  // obtenemos la direccion de la tarea iddle
  unsigned int dirTSS = (unsigned int) &tss_idle;

  // se configura el descriptor del segmento de tarea
  gdt[GDT_IDX_IDLE].base_0_15  = (unsigned short)  dirTSS;
  gdt[GDT_IDX_IDLE].base_23_16 = (char) (dirTSS >> 16);
  gdt[GDT_IDX_IDLE].base_31_24 = (char) (dirTSS >> 24);

  //
  // La tarea inactiva usa el mismo Directorio de Páginas del Kernel.
  tss_idle.cr3    = PD_DIR;

  tss_idle.eip    = EIP_IDLE;
  tss_idle.eflags = EFLAGS;
  

  // Para la pila de esta tarea, pedimos una página libre.
  tss_idle.esp = PD_DIR;

  tss_idle.ebp    = PD_DIR;
  tss_idle.es     = ES;
  tss_idle.cs     = CS;
  tss_idle.ss     = SS;
  tss_idle.ds     = DS;
  tss_idle.fs     = FS;
  tss_idle.gs     = GS;
}

void tss_inicializar() {
   configurar_tarea_inicial();
   configurar_tarea_idle();

}


